package com.example.experimen2.algorithm;

import android.util.Log;

import com.example.experimen2.data.Fruit;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;

/**
 * 游戏用到的算法
 *
 * @author : bugu
 * @date : 2023-04-02 16:44
 */
public class FruitLink {
    static boolean DEBUG = true;
    private static final String TAG = "GameAlgorithm";

    // 方向数组，用于表示上、下、左、右四个方向的移动
    private static final int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    /**
     * 评估两个水果之间是否能相连
     * 1. 两个图片的连线上不能有阻碍；
     * 2. 连线的转折次数不能超过2；
     * 3. 处在边界的两个图片可以绕出地图一个格子去连线。
     *
     * @param fruit1 水果1
     * @param fruit2 水果2
     * @param fruits 游戏区域表格
     * @return
     */
    public boolean evaluateFruitConnections(Fruit fruit1, Fruit fruit2, Fruit[][] fruits) {
        int[][] table = transform2Table(fruit1, fruit2, fruits);
        int x1 = fruit1.getColumn() + 1;
        int y1 = fruit1.getRow() + 1;
        int x2 = fruit2.getColumn() + 1;
        int y2 = fruit2.getRow() + 1;

        // 检查两个水果是否相同或者是否为0
        if (table[x1][y1] != table[x2][y2] || table[x1][y1] == 0 || table[x2][y2] == 0) {
            return false;
        }

        // 检查两个水果是否直接相连
        if (isDirectlyConnected(x1, y1, x2, y2, table)) {
            Log.i(TAG, "两个水果是否直接相连");
            return true;
        }

        // BFS 搜索
        return bfsConnect(x1, y1, x2, y2, table);
    }

    // 判断两个水果是否直接相连
    private boolean isDirectlyConnected(int x1, int y1, int x2, int y2, int[][] table) {
        int dx = Math.abs(x1 - x2);
        int dy = Math.abs(y1 - y2);
        return (dx == 0 && isStraightPathClear(x1, y1, y2, table)) ||
                (dy == 0 && isStraightPathClear(y1, x1, x2, table));
    }

    // 检查两点之间直线路径上是否有阻碍物
    private boolean isStraightPathClear(int fixed, int start, int end, int[][] table) {
        int step = (start < end) ? 1 : -1;
        for (int i = start + step; i != end; i += step) {
            if (table[fixed][i] != 0) {
                return false;
            }
        }
        return true;
    }

    // BFS 搜索路径
    public boolean bfsConnect(int x1, int y1, int x2, int y2, int[][] table) {
        boolean[][] visited = new boolean[table.length][table[0].length];
        Queue<int[]> queue = new ArrayDeque<>();
        queue.offer(new int[]{x1, y1, 0}); // {x, y, turns}

        while (!queue.isEmpty()) {
            int[] current = queue.poll();
            int x = current[0];
            int y = current[1];
            int turns = current[2];

            // 达到终点
            if (x == x2 && y == y2 && turns <= 2) {
                return true;
            }

            // 上、下、左、右四个方向
            for (int[] dir : directions) {
                int newX = x + dir[0];
                int newY = y + dir[1];

                // 跳过超出边界的情况或者已经访问过的位置
                if (newX < 0 || newX >= table.length || newY < 0 || newY >= table[0].length || visited[newX][newY]) {
                    continue;
                }

                // 如果遇到了水果2，或者当前格子是空的，或者当前格子能够直线连接，或者能够通过一个转折点连接
                if ((newX == x2 && newY == y2) ||
                        table[newX][newY] == 0 ||
                        (newX == x && isStraightPathClear(y, Math.min(y, newY), Math.max(y, newY), table)) ||
                        (turns < 2 && isDirectlyConnected(x, y, newX, newY, table))) {
                    queue.offer(new int[]{newX, newY, (newX == x || newY == y) ? turns : turns + 1});
                    visited[newX][newY] = true;
                }
            }
        }

        return false;
    }

    /**
     * 转变成可供算法使用的地图，假如原来是4x4的Fruit[][]：
     * [
     * [{fruit},{fruit},{fruit},{fruit}],
     * [{fruit},{fruit},{fruit},{fruit}],
     * [{fruit},{fruit},{fruit},{fruit}],
     * [{fruit},{fruit},{fruit},{fruit}]
     * ]
     * 现转变为6x6（在原来的外围加了一圈0，用于判断处在边界的两个图片是否能连通）的int[][]：
     * [
     * [0,0,0,0,0,0],
     * [0,1,1,1,9,0],
     * [0,1,1,1,1,0],
     * [0,1,1,1,1,0],
     * [0,1,1,1,9,0],
     * [0,0,0,0,0,0]
     * ]
     * 其中：
     * 标记0：表示没有阻挡
     * 标记1：表示有阻挡
     * 标记9：表示待消除的水果
     *
     * @param fruit1 待消除水果1
     * @param fruit2 待消除水果2
     * @param fruits 游戏区域表格
     * @return
     */
    public int[][] transform2Table(Fruit fruit1, Fruit fruit2, Fruit[][] fruits) {
        //将原表格向外扩充一层，如：原来是4x4的表格，现在扩充成5x5
        //方便判断处在边界的水果是否可以消除
        int[][] table = new int[fruits.length + 2][fruits.length + 2];
        for (int i = 0; i < table.length; i++) {
            for (int j = 0; j < table[i].length; j++) {
                //将第一行和最后一行填充0
                if (i == 0 || i == table.length - 1) {
                    table[i][j] = 0;
                    continue;
                }
                if (j == 0 || j == table[i].length - 1) {
                    table[i][j] = 0;
                    continue;
                }

                Fruit fruit = fruits[i - 1][j - 1];
                if (fruit != null) {
                    table[i][j] = 1;
                    //map[i][j] = table[i - 1][j - 1].getImageId();
                } else {
                    table[i][j] = 0;
                }
            }
        }
        table[fruit1.getRow() + 1][fruit1.getColumn() + 1] = 9;
        table[fruit2.getRow() + 1][fruit2.getColumn() + 1] = 9;
        debugPrint(fruit1, fruit2, table);
        return table;
    }

    /**
     * 打印表格到Logcat，方便游戏运行时查看游戏区域，以此辅助判断有咩有bug
     *
     * @param table 游戏区域表格
     */
    public void debugPrint(Fruit fruit1, Fruit fruit2, int[][] table) {
        //DEBUG代码：打印表格
        StringBuilder builder = new StringBuilder().append("\n");
        builder.append("int x1 = ").append(fruit1.getColumn()).append(", y1 = ").append(fruit1.getRow()).append(";");
        builder.append("\nint x2 = ").append(fruit2.getColumn()).append(",y2 =").append(fruit2.getRow()).append(";");


        builder.append("\n").append("int[][] table={");
        for (int i = 0; i < table.length; i++) {
            String line = Arrays.stream(table[i]).mapToObj(String::valueOf).reduce((a, b) -> a + "," + b).get();
            builder.append("{").append(line).append("}");
            if (i != table.length - 1) {
                builder.append(",").append("\n");
            }
        }
        builder.append("};\n");
        if (DEBUG) {
            Log.d(TAG, builder.toString());
        }
    }
}
